package cn.tedu.mall.order.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@DubboService
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {
    @Autowired
    private OmsOrderMapper omsOrderMapper;
    @Autowired
    private OmsOrderItemMapper omsOrderItemMapper;
    @Autowired
    private IOmsCartService omsCartService;
    @DubboReference
    private IForOrderSkuService dubboSkuService;

    /**
     * 新增订单
     * @param orderAddDTO 订单新增数据
     * @return 订单数据表
     */
    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {
        //一.收集信息,准备数据
        //将同名属性赋给order
        OmsOrder order = new OmsOrder();
        BeanUtils.copyProperties(orderAddDTO,order);
        //补充其他属性
        loadOrder(order);

        //为订单项OrderItem赋值
        List<OrderItemAddDTO> itemAddDTOs = orderAddDTO.getOrderItems();
        //判断订单参数中是否有至少一件商品
        if(itemAddDTOs==null || itemAddDTOs.isEmpty()){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单中必须至少包含一件商品");
        }
        List<OmsOrderItem> omsOrderItems = new ArrayList<>();
        //遍历itemAddDTOs对象 转化为OmsOrderItem类型
        for (OrderItemAddDTO itemAddDTO : itemAddDTOs) {
            OmsOrderItem orderItem = new OmsOrderItem();
            BeanUtils.copyProperties(itemAddDTO,orderItem);
            //赋值其他属性
            Long itemId = IdGeneratorUtils.getDistributeId("order_item");
            orderItem.setId(itemId);
            orderItem.setOrderId(order.getId());
            omsOrderItems.add(orderItem);

            //二.数据库操作

            //1.减少sku库存
            Long skuId = orderItem.getSkuId();
            Integer quantity = orderItem.getQuantity();
            int rows = dubboSkuService.reduceStockNum(skuId, quantity);
            if(rows==0){
                log.warn("商品skuId:{},库存不足",skuId);
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足!");
            }
            //2.删除购物车信息
            OmsCart omsCart = new OmsCart();
            omsCart.setUserId(order.getUserId());
            omsCart.setSkuId(skuId);
            omsCartService.removeUserCarts(omsCart);
        }

        //3.新增订单
        omsOrderMapper.insertOrder(order);
        log.info("新增订单:{}",order);
        //4.新增订单项
        omsOrderItemMapper.insertOrderItemList(omsOrderItems);
        log.info("新增订单项:{}",omsOrderItems);


        //三.返回订单信息
        OrderAddVO addVO = new OrderAddVO();
        addVO.setId(order.getId());
        addVO.setSn(order.getSn());
        addVO.setPayAmount(order.getAmountOfActualPay());
        addVO.setCreateTime(order.getGmtCreate());
        return addVO;
    }

    /**
     * 手动为order对象赋值
     * @param order order对象
     */
    private void loadOrder(OmsOrder order) {
        //本方法针对order对象未被赋值的属性,进行手动赋值
        //使用Leaf分布式来给id赋值
        Long id = IdGeneratorUtils.getDistributeId("order");
        order.setId(id);
        //赋值订单号
        order.setSn(UUID.randomUUID().toString());
        //为订单状态赋值
        //如果订单状态为null
        if(order.getState() == null) {
            order.setState(0);
        }
        //赋值用户id
        //做秒杀时,用户id会被赋值,需要先判断,再为其赋值
        if(order.getUserId() == null){
            order.setUserId(getUserId());
        }
        //为了保证下单时间/数据创建时间/最后修改时间一致
        //这里为它们赋值
        LocalDateTime now = LocalDateTime.now();
        order.setGmtOrder(now);
        order.setGmtCreate(now);
        order.setGmtModified(now);
        //计算实际支付金额,
        //原价
        BigDecimal price = order.getAmountOfOriginalPrice();
        //运费
        BigDecimal freight = order.getAmountOfFreight();
        //优惠
        BigDecimal discount = order.getAmountOfDiscount();
        BigDecimal actualPay = price.subtract(discount).add(freight);
        order.setAmountOfActualPay(actualPay);
    }

    /**
     * 根据订单id,修改订单状态
     * @param orderStateUpdateDTO 订单修改对象
     */
    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        OmsOrder omsOrder = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO,omsOrder);
        omsOrderMapper.updateOrderById(omsOrder);
    }

    /**
     * 查询当前登录用户指定时间范围内的所有订单
     * @param orderListTimeDTO 查询订单时间的对象
     * @return 订单列表
     */
    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        // 业务逻辑层要判断用户指定的时间信息,必须保证它们合理才能进行后面的操作
        // 编写一个方法,来判断时间的可用
        validateTimeAndLoadTimes(orderListTimeDTO);
        // 获得用户Id
        Long userId=getUserId();
        // 将用户Id赋值到参数中
        orderListTimeDTO.setUserId(userId);
        // 设置分页条件
        PageHelper.startPage(orderListTimeDTO.getPage(),
                orderListTimeDTO.getPageSize());
        // 调用mapper编写的关联查询方法
        List<OrderListVO> list=omsOrderMapper.
                selectOrdersBetweenTimes(orderListTimeDTO);
        // 别忘了返回!!!
        return JsonPage.restPage(new PageInfo<>(list));
    }

    /**
     * 验证指定范围时间
     * @param orderListTimeDTO 范围时间对象
     */
    private void validateTimeAndLoadTimes(OrderListTimeDTO orderListTimeDTO) {
        // 获取参数中的开始和结束时间
        LocalDateTime start=orderListTimeDTO.getStartTime();
        LocalDateTime end=orderListTimeDTO.getEndTime();
        // 为了不在增加业务的复杂度,我们设计当start和end中有一个为null时就查询最近一个月的订单
        if(start==null || end ==null){
            // 设置开始时间为当前时间的前一个月
            start=LocalDateTime.now().minusMonths(1);
            end=LocalDateTime.now();
            // 赋值到参数中
            orderListTimeDTO.setStartTime(start);
            orderListTimeDTO.setEndTime(end);
        }else{
            // 如果start和end都非空
            // 要判断start是否小于end,如果end小于start要抛异常
            // 如果编写支持国际不同时区的时间判断,在比较时要添加时区的修正
            if(end.toInstant(ZoneOffset.of("+8")).toEpochMilli()<
                    start.toInstant(ZoneOffset.of("+8")).toEpochMilli()){
                // 如果判断结果表示结束时间小于开始时间,抛出异常,终止方法
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,
                        "结束时间应大于起始时间");
            }
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }


    public CsmallAuthenticationInfo getUserInfo(){
        // 编码获得SpringSecurity上下文中保存的权限
        UsernamePasswordAuthenticationToken authenticationToken =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder
                        .getContext().getAuthentication();
        //判断获得的信息是不是null
        if (authenticationToken == null) {
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"请先登录!");
        }
        //上下文信息确定存在后,获取其中的用户信息
        CsmallAuthenticationInfo csmallAuthenticationInfo =
                (CsmallAuthenticationInfo) authenticationToken.getCredentials();
        return csmallAuthenticationInfo;

    }
    //获取用户的id的方法,
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
